@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)

import kotlinx.cinterop.*
import platform.posix.*
import kotlin.math.sqrt

class Body(
    var x: Double,
    var y: Double,
    var z: Double,
    var vx: Double,
    var vy: Double,
    var vz: Double,
    var mass: Double
)

object NBodySimulation {
    private const val PI = 3.141592653589793
    private val SOLAR_MASS = 4 * PI * PI
    private const val DAYS_PER_YEAR = 365.24

    /**
     * 修复的数字格式化函数，确保正确处理负数并保留9位小数
     */
    private fun formatTo9Decimals(value: Double): String {
        // 处理负数情况
        val isNegative = value < 0
        val absValue = if (isNegative) -value else value

        // 扩大10^9倍并四舍五入
        val scaled = absValue * 1_000_000_000 + 0.5
        val longValue = scaled.toLong()

        // 分离整数和小数部分
        val integerPart = longValue / 1_000_000_000
        val fractionalPart = longValue % 1_000_000_000

        // 确保小数部分始终是9位数字，不足补零
        val fractionalStr = fractionalPart.toString().padStart(9, '0').take(9)

        // 组合结果，添加负号（如果需要）
        return if (isNegative) {
            "-$integerPart.$fractionalStr"
        } else {
            "$integerPart.$fractionalStr"
        }
    }

    private fun offsetMomentum(bodies: Array<Body>) {
        var px = 0.0
        var py = 0.0
        var pz = 0.0

        bodies.forEach { body ->
            px += body.vx * body.mass
            py += body.vy * body.mass
            pz += body.vz * body.mass
        }

        bodies[0].vx = -px / SOLAR_MASS
        bodies[0].vy = -py / SOLAR_MASS
        bodies[0].vz = -pz / SOLAR_MASS
    }

    private fun calculateEnergy(bodies: Array<Body>): Double {
        var energy = 0.0
        val numBodies = bodies.size

        for (i in 0 until numBodies) {
            val body = bodies[i]
            // 动能
            energy += 0.5 * body.mass * (body.vx * body.vx + body.vy * body.vy + body.vz * body.vz)

            // 势能
            for (j in i + 1 until numBodies) {
                val dx = body.x - bodies[j].x
                val dy = body.y - bodies[j].y
                val dz = body.z - bodies[j].z
                val distance = sqrt(dx * dx + dy * dy + dz * dz)
                energy -= (body.mass * bodies[j].mass) / distance
            }
        }
        return energy
    }

    private fun advanceSimulation(bodies: Array<Body>, dt: Double) {
        val numBodies = bodies.size

        // 计算作用力并更新速度
        for (i in 0 until numBodies) {
            for (j in i + 1 until numBodies) {
                val dx = bodies[i].x - bodies[j].x
                val dy = bodies[i].y - bodies[j].y
                val dz = bodies[i].z - bodies[j].z
                val distanceSquared = dx * dx + dy * dy + dz * dz
                val distance = sqrt(distanceSquared)
                val force = dt / (distanceSquared * distance)

                // 更新速度
                bodies[i].vx -= dx * bodies[j].mass * force
                bodies[i].vy -= dy * bodies[j].mass * force
                bodies[i].vz -= dz * bodies[j].mass * force

                bodies[j].vx += dx * bodies[i].mass * force
                bodies[j].vy += dy * bodies[i].mass * force
                bodies[j].vz += dz * bodies[i].mass * force
            }
        }

        // 更新位置
        bodies.forEach { body ->
            body.x += body.vx * dt
            body.y += body.vy * dt
            body.z += body.vz * dt
        }
    }

    private fun runSimulation(iterations: Int) {
        // 初始化天体数据
        val bodies = arrayOf(
            // 太阳
            Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS),
            // 木星
            Body(
                4.84143144246472090e+00,
                -1.16032004402742839e+00,
                -1.03622044471123109e-01,
                1.66007664274403694e-03 * DAYS_PER_YEAR,
                7.69901118419740425e-03 * DAYS_PER_YEAR,
                -6.90460016972063023e-05 * DAYS_PER_YEAR,
                9.54791938424326609e-04 * SOLAR_MASS
            ),
            // 土星
            Body(
                8.34336671824457987e+00,
                4.12479856412430479e+00,
                -4.03523417114321381e-01,
                -2.76742510726862411e-03 * DAYS_PER_YEAR,
                4.99852801234917238e-03 * DAYS_PER_YEAR,
                2.30417297573763929e-05 * DAYS_PER_YEAR,
                2.85885980666130812e-04 * SOLAR_MASS
            ),
            // 天王星
            Body(
                1.28943695621391310e+01,
                -1.51111514016986312e+01,
                -2.23307578892655734e-01,
                2.96460137564761618e-03 * DAYS_PER_YEAR,
                2.37847173959480950e-03 * DAYS_PER_YEAR,
                -2.96589568540237556e-05 * DAYS_PER_YEAR,
                4.36624404335156298e-05 * SOLAR_MASS
            ),
            // 海王星
            Body(
                1.53796971148509165e+01,
                -2.59193146099879641e+01,
                1.79258772950371181e-01,
                2.68067772490389322e-03 * DAYS_PER_YEAR,
                1.62824170038242295e-03 * DAYS_PER_YEAR,
                -9.51592254519715870e-05 * DAYS_PER_YEAR,
                5.15138902046611451e-05 * SOLAR_MASS
            )
        )

        // 抵消动量
        offsetMomentum(bodies)

        // 输出初始能量
        println(formatTo9Decimals(calculateEnergy(bodies)))

        // 运行模拟迭代（使用正确的迭代次数100000才能得到预期结果）
        repeat(iterations) {
            advanceSimulation(bodies, 0.01)
        }

        // 输出最终能量
        println(formatTo9Decimals(calculateEnergy(bodies)))
    }

    fun execute(args: Array<String>) {
        // 创建文件的辅助函数
        fun createFile(filename: String) {
            val file = fopen(filename, "w")
            if (file != null) {
                fclose(file)
            } else {
                println("无法创建文件: $filename")
            }
        }

        try {
            // 创建标记文件
            createFile("./one")

            // 获取迭代次数，默认为100000（这是得到正确结果的关键）
            val iterations = if (args.isNotEmpty()) args[0].toInt() else 100000
            runSimulation(iterations)

            // 创建标记文件
            createFile("./two")
        } catch (e: NumberFormatException) {
            println("无效的数字格式: ${e.message}")
        }
    }
}

// 程序入口点
fun main(args: Array<String>) {
    NBodySimulation.execute(args)
}
    